package com.gateway.security.authentication.convert;

import com.gateway.common.dto.ApiDefinition;
import com.gateway.common.utils.Singleton;
import com.gateway.plugin.base.condition.data.ParameterDataFactory;
import com.gateway.security.common.exception.TokenAuthenticationException;
import com.gateway.security.common.token.CookieServerSecurityTokenRepository;
import com.gateway.security.common.token.DefaultSecurityToken;
import com.gateway.security.common.token.HeaderServerSecurityTokenRepository;
import com.gateway.security.common.token.QueryServerSecurityTokenRepository;
import com.gateway.security.common.token.SecurityToken;
import com.gateway.security.common.token.ServerSecurityTokenRepository;
import com.gateway.security.config.SecurityConfig;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.Map;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Function;

/**
 * @program: api-gateway
 * @description:
 * @author: YuKai Fan
 * @create: 2025/2/12 16:00
 **/
public class ConfigServerSecurityTokenRepository implements ServerSecurityTokenRepository {

    private final static Flux<ServerSecurityTokenRepository> repositories = Flux.just(
            new HeaderServerSecurityTokenRepository(),
            new CookieServerSecurityTokenRepository(),
            new QueryServerSecurityTokenRepository()
    );


    private final BiFunction<ServerWebExchange, ApiDefinition, Mono<SecurityToken> > defaultLoadTokenFunc =
            (exchange, apiDefinition) -> repositories
                    .concatMap(repository -> repository.loadToken(exchange, apiDefinition))
                    .filter(Objects::nonNull)
                    .next()
                    .switchIfEmpty(Mono.error(new TokenAuthenticationException("不存在认证标识")));


    @Override
    public Mono<SecurityToken> loadToken(ServerWebExchange exchange, ApiDefinition apiDefinition) {
        SecurityConfig securityConfig = Singleton.INST.get(SecurityConfig.class);
        if (Objects.isNull(securityConfig)) {
            return defaultLoadTokenFunc.apply(exchange, apiDefinition);
        }
        Map<String, SecurityConfig.Config> configMap = securityConfig.getConfigMap();
        SecurityConfig.Config config = configMap.get(apiDefinition.getAuthenticationType().getType());
        if (Objects.isNull(config)) {
            return defaultLoadTokenFunc.apply(exchange, apiDefinition);

        }
        return Mono.just(
                        ParameterDataFactory.builderData(config.getParamType(), config.getTokenName(), exchange)
                ).filter(StringUtils::isNotBlank)
                .flatMap(token -> Mono.just(createSecurityToken(token, config.getParamType(), config.getTokenName())))
                .switchIfEmpty(Mono.error(new TokenAuthenticationException("不存在认证标识")));
    }

    private SecurityToken createSecurityToken(String token, String paramType, String paramName) {
        return new DefaultSecurityToken(token, paramType, paramName);
    }
}
